package com.slushpupie.deskclock;

import android.R.integer;
import android.R.string;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.accounts.AccountManagerCallback;
import android.accounts.AccountManagerFuture;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ListActivity;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.PowerManager;
import android.preference.PreferenceManager;
import android.text.TextUtils;
import android.text.format.DateFormat;
import android.text.format.Time;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Gravity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.Window;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
import android.widget.ArrayAdapter;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;

import java.io.IOException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.logging.Logger;

import com.google.api.client.extensions.android2.AndroidHttp;
import com.google.api.client.googleapis.auth.oauth2.draft10.GoogleAccessProtectedResource;
import com.google.api.client.googleapis.extensions.android2.auth.GoogleAccountManager;
import com.google.api.client.http.HttpResponse;
import com.google.api.client.http.HttpResponseException;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.json.JsonHttpRequest;
import com.google.api.client.http.json.JsonHttpRequestInitializer;
import com.google.api.client.json.jackson.JacksonFactory;
import com.google.api.services.tasks.Tasks;
import com.google.api.services.tasks.TasksRequest;
import com.google.api.services.tasks.model.Task;

public class DeskClock extends ListActivity implements
		SharedPreferences.OnSharedPreferenceChangeListener {

	private static final String LOG_TAG = "DeskClock";
	private static char digitcharset[] = { '0', '1', '2', '3', '4', '5', '6',
			'7', '8', '9' };
	private static char lettercharset[] = { 'A', 'P' };

	private Typeface[] fonts;

	private PowerManager.WakeLock wl = null;
	private boolean isRunning = false;
	private RefreshHandler handler = new RefreshHandler();

	// current state
	// private TextView display;
	private DisplayView display;
	private RelativeLayout layout;
	private int displayWidth = -1;
	private int displayHeight = -1;;
	private boolean needsResizing = false;

	
	
	// backed by preferences

	private String lastChangelog = "";

	private static final String AUTH_TOKEN_TYPE = "Manage your tasks";

	private static final String PREF = "MyPrefs";
	private static final int DIALOG_ACCOUNTS = 1;
	private static final int MENU_ACCOUNTS = 0;
	public static final int REQUEST_AUTHENTICATE = 0;

	private final HttpTransport transport = AndroidHttp
			.newCompatibleTransport();

	private Handler scrollHandler = new Handler();
	private Tasks serviceTask;
	private GoogleAccessProtectedResource accessProtectedResource = new GoogleAccessProtectedResource(null);

	private GoogleAccountManager accountManager;

	private DeskClock deskClockInstance;
	private Timer updateListTimer;
	
	private Prefs prefs;
	//private Timer updateTaskTimer = new Timer();
	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		deskClockInstance = this;
		requestWindowFeature(Window.FEATURE_NO_TITLE);
		setContentView(R.layout.main);

		getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
				WindowManager.LayoutParams.FLAG_FULLSCREEN);


		layout = (RelativeLayout) findViewById(R.id.layout_whole);
		display = (DisplayView) findViewById(R.id.display);

		setupFonts();
		
		prefs = new Prefs(PreferenceManager.getDefaultSharedPreferences(this), this);
		UpdateByPrefs(prefs);
		
		configureDisplay();
		resizeClock();

		setupTaskService();

		checkAccount();

		setupPrefs();

		Log.d(LOG_TAG, "SETUP.");
	}

	private void startUpdateListTimer() {
		updateListTimer = new Timer();
		UpdateListTask task = new UpdateListTask();
		updateListTimer.scheduleAtFixedRate(task, 2000, 2000);

		//UpdateTasksTask task2 = new UpdateTasksTask();
		//updateTaskTimer.scheduleAtFixedRate(task2, 10000, 10000);
	}

	private void setupPrefs() {
		SharedPreferences prefs = PreferenceManager
				.getDefaultSharedPreferences(this);
		prefs.registerOnSharedPreferenceChangeListener(this);
	}

	private void checkAccount() {
		accountManager = new GoogleAccountManager(this);
		gotAccount(false);
	}

	private void setupTaskService() {
		serviceTask = Tasks
				.builder(transport, new JacksonFactory())
				.setApplicationName("Google-TasksSample/1.0")
				.setHttpRequestInitializer(accessProtectedResource)
				.setJsonHttpRequestInitializer(
						new JsonHttpRequestInitializer() {

							public void initialize(JsonHttpRequest request)
									throws IOException {
								TasksRequest tasksRequest = (TasksRequest) request;
								tasksRequest.setKey(ClientCredentials.KEY);
							}
						}).build();
	}

	private void setupFonts() {
		fonts = new Typeface[15];
		fonts[0] = Typeface.DEFAULT_BOLD;
		fonts[1] = Typeface.SANS_SERIF;
		fonts[2] = Typeface.SERIF;
		fonts[3] = Typeface.MONOSPACE;
		fonts[4] = Typeface.createFromAsset(getAssets(),
				"fonts/Abduction2000.ttf");
		fonts[5] = Typeface.createFromAsset(getAssets(), "fonts/DSPoint.ttf");
		fonts[6] = Typeface
				.createFromAsset(getAssets(), "fonts/DSTerminal.ttf");
		fonts[7] = Typeface.createFromAsset(getAssets(), "fonts/DT104.ttf");
		fonts[8] = Typeface.createFromAsset(getAssets(), "fonts/Delusion.ttf");
		fonts[11] = Typeface.createFromAsset(getAssets(), "fonts/Digital2.ttf");
		fonts[12] = Typeface.createFromAsset(getAssets(),
				"fonts/DigitaldreamFat.ttf");
		fonts[13] = Typeface.createFromAsset(getAssets(),
				"fonts/DisplayDots.ttf");
		fonts[14] = Typeface.createFromAsset(getAssets(), "fonts/digi.otf");
	}

	private void gotAccount(boolean tokenExpired) {
		SharedPreferences settings = getSharedPreferences(PREF, 0);
		String accountName = settings.getString("accountName", null);
		Account account = accountManager.getAccountByName(accountName);
		if (account != null) {
			if (tokenExpired) {
				accountManager.invalidateAuthToken(accessProtectedResource
						.getAccessToken());
				accessProtectedResource.setAccessToken(null);
			}
			gotAccount(account);
			return;
		}
		
		showDialog(DIALOG_ACCOUNTS);
	}

	private void gotAccount(final Account account) {
		SharedPreferences settings = getSharedPreferences(PREF, 0);
		SharedPreferences.Editor editor = settings.edit();
		editor.putString("accountName", account.name);
		editor.commit();
		accountManager.manager.getAuthToken(account, AUTH_TOKEN_TYPE, true,
				new AccountManagerCallback<Bundle>() {

					public void run(AccountManagerFuture<Bundle> future) {
						try {
							Bundle bundle = future.getResult();
							if (bundle.containsKey(AccountManager.KEY_INTENT)) {
								Intent intent = bundle
										.getParcelable(AccountManager.KEY_INTENT);
								intent.setFlags(intent.getFlags()
										& ~Intent.FLAG_ACTIVITY_NEW_TASK);
								startActivityForResult(intent,
										REQUEST_AUTHENTICATE);
							} else if (bundle
									.containsKey(AccountManager.KEY_AUTHTOKEN)) {
								accessProtectedResource.setAccessToken(bundle
										.getString(AccountManager.KEY_AUTHTOKEN));
								onAuthToken();
							}
						} catch (Exception e) {
							handleException(e);
						}
					}
				}, null);
	}

	private void onAuthToken() {
		try {
			List<String> taskTitles = new ArrayList<String>();
			List<Task> tasks = serviceTask.tasks().list("@default").execute()
					.getItems();
			if (tasks != null) {
				for (Task task : tasks) {
					if((task.getStatus().equalsIgnoreCase("completed")) == false){
						taskTitles.add(task.getTitle());
					}
				}
			} else {
				taskTitles.add("No tasks.");
			}
			
			ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,	R.layout.listitemlayout, taskTitles);
			setListAdapter(adapter);
		} catch (IOException e) {
			handleException(e);
		}

		// setContentView(R.layout);
	}

	private void handleException(Exception e) {
		e.printStackTrace();
		if (e instanceof HttpResponseException) {
			HttpResponse response = ((HttpResponseException) e).getResponse();
			int statusCode = response.getStatusCode();
			try {
				response.ignore();
			} catch (IOException e1) {
				e1.printStackTrace();
			}
			if (statusCode == 401) {
				gotAccount(true);
				return;
			}
		}
	}

	/** Called when the activity becomes visible. */
	@Override
	public void onStart() {
		super.onStart();

		isRunning = true;
		updateTime();
		startUpdateListTimer();
	}

	/** Called when the activity is no longer visible. */
	@Override
	public void onStop() {
		setScreenLock(0); // release any wakelocks
		isRunning = false;
		stopUpdateListTimer();
		super.onStop();
	}

	private void stopUpdateListTimer() {
		updateListTimer.cancel();
		//updateTaskTimer.cancel();
	}

	/** Called before the activity is destroyed. */
	@Override
	public void onDestroy() {
		if (wl != null) {
			wl.release();
			wl = null;
		}
		
		stopUpdateListTimer();
		super.onDestroy();
	}

	/** Called on configuration changes, such as screen rotate */
	@Override
	public void onConfigurationChanged(Configuration newConfig) {
		super.onConfigurationChanged(newConfig);
		Log.d(LOG_TAG, "config change occurred");
		configureDisplay();

	}

	/** Called when first creating menu */
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		getMenuInflater().inflate(R.menu.main_menu, menu);
		return true;
	}

	/** Called when a menu item is selected */
	@Override
	public boolean onOptionsItemSelected(MenuItem menuItem) {
		if (menuItem.getItemId() == R.id.menu_prefs) {
			Intent intent = new Intent().setClass(this,
					DeskClockPreferenceActivity.class);
			startActivityForResult(intent, 0);
		}

		return true;
	}

	/** Called when a shared preference is changed, added, or removed */
	public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
			String key) {
		this.needsResizing = prefs.loadPrefs(sharedPreferences);
		UpdateByPrefs(prefs);
	}

	/* Private methods */

	

	private void setScreenLock(int keepOn) {
		if (keepOn > 0) {
			if (wl == null) {
				PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
				/*
				 * Could use SCREEN_DIM_WAKE_LOCK SCREEN_BRIGHT_WAKE_LOCK
				 */
				if (keepOn == 1) {
					wl = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK,
							"DeskClock");

					Window window = getWindow();
					LayoutParams layoutParams = window.getAttributes();
					try {
						Field buttonBrightness = layoutParams.getClass()
								.getField("buttonBrightness");
						buttonBrightness.set(layoutParams, 0);
					} catch (NoSuchFieldException e) {

					} catch (IllegalAccessException e) {

					}
					window.setAttributes(layoutParams);

					Log.d(LOG_TAG, "Using DIM wakelock");
				} else if (keepOn == 2) {
					wl = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK,
							"DeskCLock");
					Log.d(LOG_TAG, "Using BRIGHT wakelock");
				} else {
					Log.e(LOG_TAG, "Unknown wakelock value!");
					return;
				}
				wl.acquire();
				Log.d(LOG_TAG, "WakeLock acquired");
			}
		} else {
			if (wl != null) {
				Log.d(LOG_TAG, "WakeLock released");
				wl.release();
				wl = null;
			}
		}

	}

	private void configureDisplay() {
		this.getListView().setDivider(null);
		
		DisplayMetrics metrics = new DisplayMetrics();
		getWindowManager().getDefaultDisplay().getMetrics(metrics);
		displayWidth = metrics.widthPixels;
		displayHeight = metrics.heightPixels;

		// layout.setBackgroundColor(Color.WHITE);
		// display.setBackgroundColor(prefsBackgroundColor);
		display.setBackgroundColor(0);
		display.setColor(prefs.getFontColor());
		display.setScreenSaver(prefs.isScreenSaver());
		display.setAlpha(prefs.getFontAlpha());
		
		Log.d(LOG_TAG, "display configured");
		needsResizing = true;
	}

	private void resizeClock() {

		// determine largest digit
		Rect bb;
		int w;

		char bdigit = digitWithMaxSize();

		// determine largest letter
		char bletter = letterWithMaxSize();

		String str = String.format("%c%c:%c%c", bdigit, bdigit, bdigit, bdigit);

		if (prefs.isShowSeconds())
			str = String.format("%s:%c%c", str, bdigit, bdigit);
		if (prefs.isShowMeridiem())
			str = String.format("%s %cM", str, bletter);

		Rect boundingBox = new Rect(0, 0, displayWidth, displayHeight);
		float fontSize = fitTextToRect(fonts[prefs.getFont()], str, boundingBox);
		if (prefs.isScreenSaver()) {
			fontSize = fontSize * 0.8f;
		}

		int leftPadding = 0;
		Rect digitBounds = getBoundingBox("8", fonts[prefs.getFont()], fontSize);
		int width = digitBounds.width();
		leftPadding = width * -4;

		display.setWideTime(str);
		display.setFont(fonts[prefs.getFont()]);
		display.setPadding(leftPadding, 0, 0, 0);
		display.setSize(fontSize);

		needsResizing = false;
		updateTime();
	}

	private char letterWithMaxSize() {
		Rect bb;
		int w;
		char bletter = lettercharset[0];
		bb = getBoundingBox(String.valueOf(bletter), fonts[prefs.getFont()], 10);
		w = bb.width();
		for (int i = 1; i < lettercharset.length; i++) {
			bb = getBoundingBox(String.valueOf(lettercharset[i]),
					fonts[prefs.getFont()], 10);
			if (bb.width() > w) {
				bletter = lettercharset[i];
				w = bb.width();
			}
		}
		return bletter;
	}

	private char digitWithMaxSize() {
		char bdigit = digitcharset[0];
		Rect bb = getBoundingBox(String.valueOf(bdigit), fonts[prefs.getFont()], 10);
		int w = bb.width();
		for (int i = 1; i < digitcharset.length; i++) {
			bb = getBoundingBox(String.valueOf(digitcharset[i]),
					fonts[prefs.getFont()], 10);
			if (bb.width() > w) {
				bdigit = digitcharset[i];
				w = bb.width();
			}
		}
		return bdigit;
	}

	private void updateTime() {
		if (needsResizing) {
			resizeClock();
			return;
		}

		Calendar cal = Calendar.getInstance();

		char colon = ':';
		if (prefs.isBlinkColon() && cal.get(Calendar.SECOND) % 2 == 0) {
			colon = ' ';
		}

		String format = String.format("h%cmm", colon);

		if (prefs.isMilitaryTime())
			format = String.format("k%cmm", colon);

		if (prefs.isShowSeconds())
			format = format + String.format("%css", colon);

		if (prefs.isShowMeridiem())
			format = format + " aa";

		// Log.d(LOG_TAG,"Setting time to "+localTime.format(format));

		display.setTime(DateFormat.format(format, cal));
		// layout.postInvalidate();
		if (isRunning)
			handler.tick();

	}

	private float fitTextToRect(Typeface font, String text, Rect fitRect) {

		int width = fitRect.width();
		int height = fitRect.height();

		int minGuess = 0;
		int maxGuess = 640;
		int guess = 320;

		Rect r;
		boolean lastGuessTooSmall = true;

		for (int i = 0; i < 32; i++) {

			if (minGuess + 1 == maxGuess) {
				Log.d(LOG_TAG, "Discovered font size " + minGuess);
				r = getBoundingBox(text, font, guess);
				return minGuess;
			}

			r = getBoundingBox(text, font, guess);
			if (r.width() > width || r.height() > height) {
				maxGuess = guess;
				lastGuessTooSmall = false;

			} else {
				minGuess = guess;
				lastGuessTooSmall = true;
			}
			guess = (minGuess + maxGuess) / 2;
		}

		Log.d(LOG_TAG, "Unable to discover font size");
		if (lastGuessTooSmall)
			return maxGuess;
		else
			return minGuess;

	}

	private Rect getBoundingBox(String text, Typeface font, float size) {
		Rect r = new Rect(0, 0, 0, 0);
		Paint paint = new Paint();
		paint.setTypeface(font);
		paint.setTextSize(size);
		paint.getTextBounds(text, 0, text.length(), r);
		return r;
	}

	protected Dialog onCreateDialog(int id) {
		switch (id) {
		case DIALOG_ACCOUNTS: {
			AlertDialog.Builder builder = new AlertDialog.Builder(this);
			builder.setTitle("Select a Google account");
			final Account[] accounts = accountManager.getAccounts();
			final int size = accounts.length;
			String[] names = new String[size];
			for (int i = 0; i < size; i++) {
				names[i] = accounts[i].name;
			}
			builder.setItems(names, new DialogInterface.OnClickListener() {
				public void onClick(DialogInterface dialog, int which) {
					gotAccount(accounts[which]);
				}
			});
			return builder.create();
		}
		default:
			return null;
		}
	}

	private void UpdateByPrefs(Prefs prefs){
		setScreenLock(prefs.getKeepSreenOn());
		setRequestedOrientation(prefs.getScreenOrientation());
		display.setAlpha(prefs.getFontAlpha());
		display.setColor(prefs.getFontColor());
		layout.setBackgroundColor(prefs.getBackgroundColor());
		display.setScreenSaver(prefs.isScreenSaver());
	}
	
	private class UpdateTasksTask extends TimerTask{
		@Override
		public void run() {
			onAuthToken();
		}
	}

	
private class UpdateListTask extends TimerTask{
	private int pos1 = 0;
	
	@Override
	public void run() {
		pos1 = deskClockInstance.getListView().getFirstVisiblePosition();
		if(deskClockInstance.getListView().getLastVisiblePosition() >= deskClockInstance.getListView().getCount()-1){
			pos1 = 0;
		}else{
			pos1 = deskClockInstance.getListView().getLastVisiblePosition();			
		}
		
		//Log.d(LOG_TAG, String.format("Tick, count:%1$s, pos:%2$s",some.getListView().getCount()-1, pos1));
		deskClockInstance.getListView().smoothScrollToPosition(pos1);
	}
}
	
	private class RefreshHandler extends Handler {
		public void handleMessage(Message message) {
			updateTime();
		}

		public void tick() {
			this.removeMessages(0);
			sendMessageDelayed(obtainMessage(0), 250);
		}
	}
}
